#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#include "sample_comm.h"
#include "od_def.h"
#include "od_trace.h"
#include "od_task.h"
#include "od_tube.h"
#include "od_os.h"
#include "od_msg.h"
#include "od_os_config.h"
#include "od_engine.h"
#include "HisiDecEncCtrl.h"


#define HISI_DEC_ENC_CTRL_TSK_STACK_SIZE    (4*1024 * 1024)   /*线程栈资源大小*/

#define HIOUT_ASSERT_RETURN(expr,retval)    \
        do { \
        if (!(expr)) { assert(expr); return retval; } \
        } while (0)


typedef struct
{
    TASK_COMMON_MEMBERS

    bool wbcCfgDone;  //wbc是回写单元
    int *cfg_done;
    struct { //HiMPP-API core parameter config!
        PIC_SIZE_E enDispPicSize;    /**< 拼接基底画布尺寸*/
        VO_INTF_SYNC_E enIntfSync;  
        SIZE_S stDispSize;
        VB_CONFIG_S stVbConfig;
        HI_U32 u32VdecChnNum;
        HI_U32 VpssGrpNum;
        VPSS_GRP_ATTR_S stVpssGrpAttr;
        VPSS_GRP VpssGrp;
        SAMPLE_VO_CONFIG_S stVoConfig;
        VO_LAYER VoLayer;
        SAMPLE_VO_WBC_CONFIG    stWbcConfig;
        SAMPLE_VDEC_ATTR astSampleVdec[VDEC_MAX_CHN_NUM];
        HI_BOOL abChnEnable[VPSS_MAX_CHN_NUM];
        VPSS_CHN_ATTR_S astVpssChnAttr[VPSS_MAX_CHN_NUM];
        SAMPLE_VO_MODE_E enVoMode;
    }himpp;
}hisiDecEncCtrl_obj;


params_def hisiDecEncCtrl_dynamic_params_def[]=
{
    {OFFSET(hisiDecEncCtrl_dynamic_params, enPayLoad), PT_VAR_INT32 | PT_START_STOP, sizeof(int), PT_H264, PT_H265},
    {OFFSET(hisiDecEncCtrl_dynamic_params, enSize),    PT_VAR_INT32 | PT_REAL_TIME, sizeof(int), PIC_CIF, PIC_BUTT-1},
    {OFFSET(hisiDecEncCtrl_dynamic_params, u32Gop),    PT_VAR_INT32 | PT_START_STOP, sizeof(int), 1, 100},
    {OFFSET(hisiDecEncCtrl_dynamic_params, u32FrameRate),    PT_VAR_INT32 | PT_START_STOP, sizeof(int), 1, 60},
    {OFFSET(hisiDecEncCtrl_dynamic_params, u32BitRate),      PT_VAR_INT32 | PT_START_STOP, sizeof(int), 64, (120 * 1024)},
    {OFFSET(hisiDecEncCtrl_dynamic_params, enRcMode),        PT_VAR_INT32 | PT_START_STOP, sizeof(int), VENC_RC_MODE_H264CBR, VENC_RC_MODE_BUTT-1},
    {OFFSET(hisiDecEncCtrl_dynamic_params, enDecMode),       PT_VAR_INT32 | PT_START_STOP, sizeof(int), VIDEO_DEC_MODE_IPB, VIDEO_DEC_MODE_BUTT-1},
    {0, 0, 0, 0, 0}
};

const hisiDecEncCtrl_static_params glb_hisiDecEncCtrl_static_params_default = 
{
    "hisi-DEC-ENC-CTRL"
};

const hisiDecEncCtrl_dynamic_params glb_hisiDecEncCtrl_dynamic_params_default = 
{
    PT_H264,               //enPayLoad 
    PIC_1080P,             //enSize
    30,                    //u32Gop
    25,                    //u32FrameRate
    (25 * 1024),           //u32BitRate
    SAMPLE_RC_CBR,         //enRcMode
    VIDEO_DEC_MODE_IP      //enDecMode
};

static HI_S32 SAMPLE_COMM_VO_WBC_BindVenc(VO_LAYER  SrcVoLayer, VO_CHN SrcVoChn, VENC_CHN VencChn)
{
    MPP_CHN_S stSrcChn, stDestChn;
    stSrcChn.enModId    = HI_ID_VO;
    stSrcChn.s32DevId   = SrcVoLayer;
    stSrcChn.s32ChnId   = SrcVoChn;
    
    stDestChn.enModId  = HI_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = VencChn;

    CHECK_RET(HI_MPI_SYS_Bind(&stSrcChn, &stDestChn), "HI_MPI_SYS_Bind(VPSS-VO)");
    return HI_SUCCESS;
}

static HI_S32 SAMPLE_COMM_VO_WBC_unBindVenc(VO_LAYER  SrcVoLayer, VO_CHN SrcVoChn, VENC_CHN VencChn)
{
    MPP_CHN_S stSrcChn, stDestChn;
    stSrcChn.enModId    = HI_ID_VO;
    stSrcChn.s32DevId   = SrcVoLayer;
    stSrcChn.s32ChnId   = SrcVoChn;
    
    stDestChn.enModId  = HI_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = VencChn;

    CHECK_RET(HI_MPI_SYS_UnBind(&stSrcChn, &stDestChn), "HI_MPI_SYS_UnBind(VPSS-VO)");
    return HI_SUCCESS;
}

static void hisiDecEncCtrl_default(hisiDecEncCtrl_obj *obj)
{
    obj->wbcCfgDone = false;
    obj->himpp.enDispPicSize = PIC_1080P;
    obj->himpp.enIntfSync = VO_OUTPUT_1080P30;
    obj->himpp.u32VdecChnNum = obj->himpp.VpssGrpNum = 9;
}

static int hisiDecEncCtrl_start_stop(hisiDecEncCtrl_obj *obj)
{
    hisiDecEncCtrl_dynamic_params *params = obj->params;
    HI_S32 s32Ret, i;

    hisiDecEncCtrl_default(obj);
    
    s32Ret =  SAMPLE_COMM_SYS_GetPicSize(obj->himpp.enDispPicSize, 
                                         &obj->himpp.stDispSize);
    if(s32Ret != HI_SUCCESS) {
        RPTERR("sys get pic size fail for %#x!\n", s32Ret);
        goto END1;
    }

    memset(&obj->himpp.stVbConfig, 0, sizeof(VB_CONFIG_S));
    obj->himpp.stVbConfig.u32MaxPoolCnt             = 1;
    obj->himpp.stVbConfig.astCommPool[0].u32BlkCnt  = 10 * obj->himpp.u32VdecChnNum;
    obj->himpp.stVbConfig.astCommPool[0].u64BlkSize = COMMON_GetPicBufferSize(obj->himpp.stDispSize.u32Width, 
                                                                              obj->himpp.stDispSize.u32Height,
                                                                              PIXEL_FORMAT_YVU_SEMIPLANAR_420, 
                                                                              DATA_BITWIDTH_8, 
                                                                              COMPRESS_MODE_SEG, 
                                                                              0);
    s32Ret = SAMPLE_COMM_SYS_Init(&obj->himpp.stVbConfig);
    if(s32Ret != HI_SUCCESS) {
        RPTERR("init sys fail for %#x!\n", s32Ret);
        goto END1;
    }

    /************************************************
    step2:  init module VB or user VB(for VDEC)
    *************************************************/
    for(i=0; i<obj->himpp.u32VdecChnNum; i++) {
        obj->himpp.astSampleVdec[i].enType                           = params->enPayLoad;
        obj->himpp.astSampleVdec[i].u32Width                         = 3840;
        obj->himpp.astSampleVdec[i].u32Height                        = 2160;
        obj->himpp.astSampleVdec[i].enMode                           = VIDEO_MODE_STREAM;
        obj->himpp.astSampleVdec[i].stSapmleVdecVideo.enDecMode      = params->enDecMode;
        obj->himpp.astSampleVdec[i].stSapmleVdecVideo.enBitWidth     = DATA_BITWIDTH_8;
        obj->himpp.astSampleVdec[i].stSapmleVdecVideo.u32RefFrameNum = 3;
        obj->himpp.astSampleVdec[i].u32DisplayFrameNum               = 2;
        obj->himpp.astSampleVdec[i].u32FrameBufCnt = obj->himpp.astSampleVdec[i].stSapmleVdecVideo.u32RefFrameNum
                                                     + obj->himpp.astSampleVdec[i].u32DisplayFrameNum + 1;
    }
    s32Ret = SAMPLE_COMM_VDEC_InitVBPool(obj->himpp.u32VdecChnNum, &obj->himpp.astSampleVdec[0]);
    if(s32Ret != HI_SUCCESS) {
        RPTERR("init mod common vb fail for %#x!\n", s32Ret);
        goto END2;
    }

    /************************************************
    step3:  start VDEC
    *************************************************/
    s32Ret = SAMPLE_COMM_VDEC_Start(obj->himpp.u32VdecChnNum, &obj->himpp.astSampleVdec[0]);
    if(s32Ret != HI_SUCCESS) {
        RPTERR("start VDEC fail for %#x!\n", s32Ret);
        goto END3;
    }

    /************************************************
    step4:  start VPSS
    *************************************************/
    obj->himpp.stVpssGrpAttr.u32MaxW = 3840;
    obj->himpp.stVpssGrpAttr.u32MaxH = 2160;
    obj->himpp.stVpssGrpAttr.stFrameRate.s32SrcFrameRate = -1;
    obj->himpp.stVpssGrpAttr.stFrameRate.s32DstFrameRate = -1;
    obj->himpp.stVpssGrpAttr.enDynamicRange = DYNAMIC_RANGE_SDR8;
    obj->himpp.stVpssGrpAttr.enPixelFormat  = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    obj->himpp.stVpssGrpAttr.bNrEn   = HI_FALSE;

    memset(obj->himpp.abChnEnable, 0, sizeof(obj->himpp.abChnEnable));
    obj->himpp.abChnEnable[0] = HI_TRUE;
    obj->himpp.astVpssChnAttr[0].u32Width                    = obj->himpp.stDispSize.u32Width;
    obj->himpp.astVpssChnAttr[0].u32Height                   = obj->himpp.stDispSize.u32Height;
    obj->himpp.astVpssChnAttr[0].enChnMode                   = VPSS_CHN_MODE_AUTO;
    obj->himpp.astVpssChnAttr[0].enCompressMode              = COMPRESS_MODE_SEG;
    obj->himpp.astVpssChnAttr[0].enDynamicRange              = DYNAMIC_RANGE_SDR8;
    obj->himpp.astVpssChnAttr[0].enPixelFormat               = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    obj->himpp.astVpssChnAttr[0].stFrameRate.s32SrcFrameRate = -1;
    obj->himpp.astVpssChnAttr[0].stFrameRate.s32DstFrameRate = -1;
    obj->himpp.astVpssChnAttr[0].u32Depth                    = 0;
    obj->himpp.astVpssChnAttr[0].bMirror                     = HI_FALSE;
    obj->himpp.astVpssChnAttr[0].bFlip                       = HI_FALSE;
    obj->himpp.astVpssChnAttr[0].stAspectRatio.enMode        = ASPECT_RATIO_NONE;
    obj->himpp.astVpssChnAttr[0].enVideoFormat               = VIDEO_FORMAT_LINEAR;
    for(i=0; i<obj->himpp.u32VdecChnNum; i++) {
        obj->himpp.VpssGrp = i;
        s32Ret = SAMPLE_COMM_VPSS_Start(obj->himpp.VpssGrp, 
                                        &obj->himpp.abChnEnable[0], 
                                        &obj->himpp.stVpssGrpAttr, 
                                        &obj->himpp.astVpssChnAttr[0]);
        if(s32Ret != HI_SUCCESS) {
            RPTERR("start VPSS fail for %#x!\n", s32Ret);
            goto END4;
        }
    }

    return OD_OK;

END4:
    for(i = obj->himpp.VpssGrp; i >= 0; i--) {
        obj->himpp.VpssGrp = i;
        SAMPLE_COMM_VPSS_Stop(obj->himpp.VpssGrp, &obj->himpp.abChnEnable[0]);
    }
    
END3:
    SAMPLE_COMM_VDEC_Stop(obj->himpp.u32VdecChnNum);

END2:
    SAMPLE_COMM_VDEC_ExitVBPool();

END1:
    SAMPLE_COMM_SYS_Exit();

    return OD_ERROR;
}

static void hisiDecEncCtrl_start_stop_delete(hisiDecEncCtrl_obj *obj)
{
    HI_S32 i;

    for(i = obj->himpp.VpssGrp; i >= 0; i--) {
        obj->himpp.VpssGrp = i;
        SAMPLE_COMM_VPSS_Stop(obj->himpp.VpssGrp, &obj->himpp.abChnEnable[0]);
    }
    
    SAMPLE_COMM_VDEC_Stop(obj->himpp.u32VdecChnNum);
    SAMPLE_COMM_VDEC_ExitVBPool();
    SAMPLE_COMM_SYS_Exit();
}

static int hisiDecEncCtrl_malloc_free(hisiDecEncCtrl_obj *obj)
{
    hisiDecEncCtrl_dynamic_params *params = obj->params;
    HI_S32 s32Ret, i;
    
    /************************************************
    step5:  start VO
    *************************************************/
    obj->himpp.stVoConfig.VoDev                 = SAMPLE_VO_DEV_UHD;
    obj->himpp.stVoConfig.enVoIntfType          = VO_INTF_HDMI;
    obj->himpp.stVoConfig.enIntfSync            = obj->himpp.enIntfSync;
    obj->himpp.stVoConfig.enPicSize             = obj->himpp.enDispPicSize;
    obj->himpp.stVoConfig.u32BgColor            = COLOR_RGB_BLUE;
    obj->himpp.stVoConfig.u32DisBufLen          = 3;
    obj->himpp.stVoConfig.enDstDynamicRange     = DYNAMIC_RANGE_SDR8;
    obj->himpp.stVoConfig.enVoMode              = obj->himpp.enVoMode;
    obj->himpp.stVoConfig.enPixFormat           = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    obj->himpp.stVoConfig.stDispRect.s32X       = 0;
    obj->himpp.stVoConfig.stDispRect.s32Y       = 0;
    obj->himpp.stVoConfig.stDispRect.u32Width   = obj->himpp.stDispSize.u32Width;
    obj->himpp.stVoConfig.stDispRect.u32Height  = obj->himpp.stDispSize.u32Height;
    obj->himpp.stVoConfig.stImageSize.u32Width  = obj->himpp.stDispSize.u32Width;
    obj->himpp.stVoConfig.stImageSize.u32Height = obj->himpp.stDispSize.u32Height;
    obj->himpp.stVoConfig.enVoPartMode          = VO_PART_MODE_SINGLE;
    s32Ret = SAMPLE_COMM_VO_StartVO(&obj->himpp.stVoConfig);
    if(s32Ret != HI_SUCCESS) {
        RPTERR("start VO fail for %#x!\n", s32Ret);
        goto END5;
    }

    /************************************************
    step6:  VDEC bind VPSS
    *************************************************/
    for(i=0; i<obj->himpp.u32VdecChnNum; i++) {
        s32Ret = SAMPLE_COMM_VDEC_Bind_VPSS(i, i);
        if(s32Ret != HI_SUCCESS) {
            RPTERR("vdec bind vpss fail for %#x!\n", s32Ret);
            goto END6;
        }
    }

    /************************************************
    step7:  VPSS bind VO
    *************************************************/
    obj->himpp.VoLayer = obj->himpp.stVoConfig.VoDev;
    for(i=0; i<obj->himpp.VpssGrpNum; i++) {
        s32Ret = SAMPLE_COMM_VPSS_Bind_VO(i, 0, obj->himpp.VoLayer, i);
        if(s32Ret != HI_SUCCESS) {
            RPTERR("vpss bind vo fail for %#x!\n", s32Ret);
            goto END7;
        }
    }

    return OD_OK;
    
END7:
    for(i=0; i<obj->himpp.VpssGrpNum; i++) {
        s32Ret = SAMPLE_COMM_VPSS_UnBind_VO(i, 0, obj->himpp.VoLayer, i);
        if(s32Ret != HI_SUCCESS) {
            RPTERR("vpss unbind vo fail for %#x!\n", s32Ret);
        }
    }

END6:
    for(i=0; i<obj->himpp.u32VdecChnNum; i++) {
        s32Ret = SAMPLE_COMM_VDEC_UnBind_VPSS(i, i);
        if(s32Ret != HI_SUCCESS) {
            RPTERR("vdec unbind vpss fail for %#x!\n", s32Ret);
        }
    }

END5:
    SAMPLE_COMM_VO_StopVO(&obj->himpp.stVoConfig);

    return OD_ERROR;
}

static void hisiDecEncCtrl_malloc_free_delete(hisiDecEncCtrl_obj *obj)
{
    HI_S32 i, s32Ret;

    for(i=0; i<obj->himpp.VpssGrpNum; i++) {
        s32Ret = SAMPLE_COMM_VPSS_UnBind_VO(i, 0, obj->himpp.VoLayer, i);
        if(s32Ret != HI_SUCCESS) {
            RPTERR("vpss unbind vo fail for %#x!\n", s32Ret);
        }
    }

    for(i=0; i<obj->himpp.u32VdecChnNum; i++) {
        s32Ret = SAMPLE_COMM_VDEC_UnBind_VPSS(i, i);
        if(s32Ret != HI_SUCCESS) {
            RPTERR("vdec unbind vpss fail for %#x!\n", s32Ret);
        }
    }

    SAMPLE_COMM_VO_StopVO(&obj->himpp.stVoConfig);
}

static int hisiDecEncCtrl_ret_set(hisiDecEncCtrl_obj *obj)
{
    hisiDecEncCtrl_dynamic_params *params = obj->params;
    VENC_GOP_ATTR_S stGopAttr;
    VENC_CHN        VencChn[1]    = {0};
    HI_U32          u32Profile = 0;
    SAMPLE_RC_E enRcMode = params->enRcMode;
    HI_U32 i, s32Ret;

    if (obj->wbcCfgDone == true) {
        SAMPLE_COMM_VO_WBC_unBindVenc(obj->himpp.stWbcConfig.VoWbc, 0, VencChn[0]);
        SAMPLE_COMM_VENC_Stop(VencChn[0]);
        SAMPLE_COMM_VO_StopWBC(&obj->himpp.stWbcConfig);
    }

    /************************************************
    step 8:  start WBC device 0
    *************************************************/
    SAMPLE_COMM_VO_GetDefWBCConfig(&obj->himpp.stWbcConfig);
    s32Ret = SAMPLE_COMM_VO_StartWBC(&obj->himpp.stWbcConfig);
    if (HI_SUCCESS != s32Ret) {
        RPTERR("SAMPLE_COMM_VO_StartWBC failed with %d!\n", s32Ret);
        goto END8;
    }

    /************************************************
    step 9: start VENC
    *************************************************/
    stGopAttr.enGopMode  = VENC_GOPMODE_NORMALP;
    stGopAttr.stNormalP.s32IPQpDelta = 3;
    s32Ret = SAMPLE_COMM_VENC_Start(VencChn[0], 
                                    params->enPayLoad, 
                                    params->enSize, 
                                    enRcMode,
                                    u32Profile,
                                    &stGopAttr);
    if (HI_SUCCESS != s32Ret) {
        RPTERR("Venc Start failed for %#x!\n", s32Ret);
        goto END9;
    }

    /************************************************
    step 10: WBC bind Venc
    *************************************************/
    s32Ret = SAMPLE_COMM_VO_WBC_BindVenc(obj->himpp.stWbcConfig.VoWbc,0, VencChn[0]);
    if (HI_SUCCESS != s32Ret) {
        RPTERR("SAMPLE_COMM_VO_StartWBC failed with %d!\n", s32Ret);
        goto END10;
    }

    if (0) {
        HI_S32          s32ChnNum     = 1;
        VDEC_THREAD_PARAM_S stVdecSend[VDEC_MAX_CHN_NUM];
        pthread_t   VdecThread[2*VDEC_MAX_CHN_NUM];

        if (0) {
            s32Ret = SAMPLE_COMM_VENC_StartGetStream(&VencChn[0], s32ChnNum);
            if (HI_SUCCESS != s32Ret) {
                RPTERR("Start to get Venc stream failed!\n");
                goto END10;
            }
        }

        if (0) {
            /************************************************
            step8:  send stream to VDEC
            *************************************************/
            for(i=0; i<obj->himpp.u32VdecChnNum; i++)
            {
                snprintf(stVdecSend[i].cFileName, sizeof(stVdecSend[i].cFileName), "stream_chn0.h264");
                snprintf(stVdecSend[i].cFilePath, sizeof(stVdecSend[i].cFilePath), "%s", "./source_file");
                stVdecSend[i].enType          = obj->himpp.astSampleVdec[i].enType;
                stVdecSend[i].s32StreamMode   = obj->himpp.astSampleVdec[i].enMode;
                stVdecSend[i].s32ChnId        = i;
                stVdecSend[i].s32IntervalTime = 1000;
                stVdecSend[i].u64PtsInit      = 0;
                stVdecSend[i].u64PtsIncrease  = 0;
                stVdecSend[i].eThreadCtrl     = THREAD_CTRL_START;
                stVdecSend[i].bCircleSend     = HI_TRUE;
                stVdecSend[i].s32MilliSec     = 0;
                stVdecSend[i].s32MinBufSize   = (obj->himpp.astSampleVdec[i].u32Width 
                                                    * obj->himpp.astSampleVdec[i].u32Height * 3)>>1;
            }
            SAMPLE_COMM_VDEC_StartSendStream(obj->himpp.u32VdecChnNum, &stVdecSend[0], &VdecThread[0]);
        }
    }

    obj->wbcCfgDone = true;
    *obj->cfg_done = OD_TRUE;
    return OD_OK;

END10:
    SAMPLE_COMM_VO_WBC_unBindVenc(obj->himpp.stWbcConfig.VoWbc, 0, VencChn[0]);

END9:
    SAMPLE_COMM_VENC_Stop(VencChn[0]);

END8:
    SAMPLE_COMM_VO_StopWBC(&obj->himpp.stWbcConfig);
    
    obj->wbcCfgDone = false;
    return OD_ERROR;
}

static int hisiDecEncCtrl_process(hisiDecEncCtrl_obj *obj)
{
    RPTDBG("run loop!");
    od_msleep(5000);
    return OD_OK;
}


hisiDecEncCtrl_handle hisiDecEncCtrl_create(hisiDecEncCtrl_static_params *static_params,
                                              hisiDecEncCtrl_dynamic_params *dynamic_params)
{
    hisiDecEncCtrl_obj *obj = NULL;
    hisiDecEncCtrl_dynamic_params *params = NULL;

    HIOUT_ASSERT_RETURN(static_params, NULL);

    obj = od_task_create((char *)static_params->task_name, sizeof(hisiDecEncCtrl_obj));
    HIOUT_ASSERT_RETURN(obj, NULL);

    obj->tab = hisiDecEncCtrl_dynamic_params_def;
    obj->params_nums = sizeof(hisiDecEncCtrl_dynamic_params_def) / sizeof(params_def) - 1;

    params = od_calloc(1, sizeof(hisiDecEncCtrl_dynamic_params));
    HIOUT_ASSERT_RETURN(params, NULL);

    if (NULL == dynamic_params)
        memcpy(params, &glb_hisiDecEncCtrl_dynamic_params_default, sizeof(hisiDecEncCtrl_dynamic_params));
    else
        memcpy(params, dynamic_params, sizeof(hisiDecEncCtrl_dynamic_params));

    
    HIOUT_ASSERT_RETURN(static_params->config_done, NULL);
    obj->cfg_done = static_params->config_done;
    obj->himpp.enVoMode = static_params->enVoMode;

    obj->params = (void *)params;
    obj->stack_size = HISI_DEC_ENC_CTRL_TSK_STACK_SIZE;
    obj->pf_start_stop  = (void *)hisiDecEncCtrl_start_stop;
    obj->pf_malloc_free = (void *)hisiDecEncCtrl_malloc_free;
    obj->pf_rt_set = (void *)hisiDecEncCtrl_ret_set;
    obj->pf_process     = (void *)hisiDecEncCtrl_process;
    obj->pf_malloc_free_delete = (void *)hisiDecEncCtrl_malloc_free_delete;
    obj->pf_start_stop_delete = (void *)hisiDecEncCtrl_start_stop_delete;
    
    RPTDBG("create success!");
    return (hisiDecEncCtrl_handle)obj;
}


/*_*/

